Option Explicit

'################################################################
' Discriptor
' DFFilter 条件定义接口实现
' 条件组合器
'################################################################


Implements IDFFilterColCondition

'################################################################
' Field
'################################################################
'---------------------------------------
' And Or 检索条件
'---------------------------------------

' 全字面量的 And 条件
' 如果没有则保存为 Nothing，以减少消耗
Private andLiteralConditions As Collection

' 只使用一个 Filter 的 And 条件
' 如果没有则保存为 Nothing，以减少消耗
'
' 发生条件移动时，条件移动到 lastCheckedSingleFilterConditions 后
' 需要手动检查当前字段的长度，如果长度是 0，则应该主动设置为 Nothing
'
' List<IDFFilterColCondition/DFFilterCompositeCondition>
Private andSingleFilterConditions As Collection
' 使用了多个 Filter 的 And 条件
' 如果没有则保存为 Nothing，以减少消耗
' List<IDFFilterColCondition/DFFilterCompositeCondition>
Private andRelationFilterConditions As Collection
' or条件
' List<DFFilterCompositeCondition>
' 如果没有则保存为 Nothing，以减少消耗
Private orConditions As Collection

'---------------------------------------
' 缓存内容
'---------------------------------------
' 保存上一次检查过的 DFFilter
Private lastCheckedSingleFilter As DFFilter

' 保存上一次检查过的 DFFilter 对应的条件
' 直接将 singleColAndConditions 中的条件对象删除并移动到这个缓存中
' 如果没有，则保存成 Nothing，以减少消耗
' List<IDFFilterColCondition/DFFilterCompositeCondition>
Private lastCheckedSingleFilterConditions As Collection

' 当前条件中使用到的 DFFilter
Private fieldUsedFilter As ISet

'################################################################
' Constructor
'################################################################
Private Sub Class_Initialize()
End Sub

Private Sub Class_Terminate()
    Set andLiteralConditions = Nothing
    Set andSingleFilterConditions = Nothing
    Set andRelationFilterConditions = Nothing
    Set orConditions = Nothing
    Set lastCheckedSingleFilter = Nothing
    Set lastCheckedSingleFilterConditions = Nothing
    Set fieldUsedFilter = Nothing
End Sub

'################################################################
' Function
'################################################################
'-------------------------------------------
' Implements IDFFilterColCondition Function
'-------------------------------------------
' 筛选策略
' 检查 DFFilter 的活动行的数据是否符合规则
' 外部不应该主动调用
Public Function IDFFilterColCondition_unsafeCheckActiveRowVal() As Boolean
    Dim c As IDFFilterColCondition

    Dim hasAndCondition As Boolean
    hasAndCondition = False
    IDFFilterColCondition_unsafeCheckActiveRowVal = True

    ' 1. 检查: 全字面量的 And 条件
    '          只要有一个是 False，则立刻结束，并返回 False
    If Not andLiteralConditions Is Nothing Then
        hasAndCondition = True
        For Each c In andLiteralConditions
            If c.unsafeCheckActiveRowVal = False Then
                IDFFilterColCondition_unsafeCheckActiveRowVal = False
                Exit For
            End If
        Next
    End If

    ' 2. 检查: 只使用一个 Filter 的 And 条件
    '          只要有一个是 False，则立刻结束，并返回 False
    '   只在前面的 And 条件为 True 时，才继续检查 !!!!!
    If IDFFilterColCondition_unsafeCheckActiveRowVal = True Then
        If Not andSingleFilterConditions Is Nothing Then
            hasAndCondition = True
            For Each c In andSingleFilterConditions
                If c.unsafeCheckActiveRowVal = False Then
                    IDFFilterColCondition_unsafeCheckActiveRowVal = False
                    Exit For
                End If
            Next
        End If
    End If

    ' 3. 检查: 使用了多个 Filter 的 And 条件
    '          只要有一个是 False，则立刻结束，并返回 False
    '   只在前面的 And 条件为 True 时，才继续检查 !!!!!
    If IDFFilterColCondition_unsafeCheckActiveRowVal = True Then
        If Not andRelationFilterConditions Is Nothing Then
            hasAndCondition = True
            For Each c In andRelationFilterConditions
                If c.unsafeCheckActiveRowVal = False Then
                    IDFFilterColCondition_unsafeCheckActiveRowVal = False
                    Exit For
                End If
            Next
        End If
    End If

    ' 4. 如果有 And 条件，并且全部检查结果都是 True，则结束
    If hasAndCondition = True And IDFFilterColCondition_unsafeCheckActiveRowVal = True Then
        IDFFilterColCondition_unsafeCheckActiveRowVal = True
        Exit Function
    End If

    If Not orConditions Is Nothing Then
        ' 5. 检查: or条件
        '          只要有一个是 True，则立刻结束，并返回 False
        For Each c In orConditions
            If c.unsafeCheckActiveRowVal = True Then
                IDFFilterColCondition_unsafeCheckActiveRowVal = True
                Exit Function
            End If
        Next

        ' 如果全部 Or 条件都是 False，将会进入这里，直接返回 False
        IDFFilterColCondition_unsafeCheckActiveRowVal = False
    End If
End Function

' 返回当前条件是不是只使用了一个 Filter
Public Function IDFFilterColCondition_isUseSingleFilter() As Boolean
    IDFFilterColCondition_isUseSingleFilter = _
        IDFFilterColCondition_getAllFilters().Count = 1
End Function

' 返回当前条件中是否使用了 Filter
' 因为存在如: And 1=1 这样的全字面量条件
Public Function IDFFilterColCondition_isUseFilter() As Boolean
    ' 只要使用的 Filter 数量大于 0，则使用了 Filter
    IDFFilterColCondition_isUseFilter = _
         IDFFilterColCondition_getAllFilters().Count > 0
End Function

' 获取当前条件中使用的所有 Filter
' @return HashSet<DFFilter>
Public Function IDFFilterColCondition_getAllFilters() As ISet
    Dim c As IDFFilterColCondition

    ' 0. 如果缓存中已经有了，则直接返回
    If Not fieldUsedFilter Is Nothing Then
        Set IDFFilterColCondition_getAllFilters = fieldUsedFilter
        Exit Function
    End If

    Set fieldUsedFilter = UtilsSet.createHashSet

    ' 1. 检查: 只使用一个 Filter 的 And 条件
    '          只要有一个是 False，则立刻结束，并返回 False
    If Not andSingleFilterConditions Is Nothing Then
        For Each c In andSingleFilterConditions
            fieldUsedFilter.MergeSet c.getAllFilters
        Next
    End If

    ' 2. 检查: 保存上一次检查过的 DFFilter 对应的条件
    '          只要有一个是 False，则立刻结束，并返回 False
    If Not lastCheckedSingleFilterConditions Is Nothing Then
        For Each c In lastCheckedSingleFilterConditions
            fieldUsedFilter.MergeSet c.getAllFilters
        Next
    End If

    ' 3. 检查: 使用了多个 Filter 的 And 条件
    '          只要有一个是 False，则立刻结束，并返回 False
    If Not andRelationFilterConditions Is Nothing Then
        For Each c In andRelationFilterConditions
            fieldUsedFilter.MergeSet c.getAllFilters
        Next
    End If

    If Not orConditions Is Nothing Then
        ' 4. 检查: or条件
        '          只要有一个是 True，则立刻结束，并返回 False
        For Each c In orConditions
            fieldUsedFilter.MergeSet c.getAllFilters
        Next
    End If

    Set IDFFilterColCondition_getAllFilters = fieldUsedFilter
End Function


' 检查当前条件使用的 DFFilter 是否和指定的 DFFilter 相同
' 如果条件中使用了多个 Filter，则返回 False
' 如果是全字面量列，则返回 False
Public Function IDFFilterColCondition_isSameFilter(ByRef filter As DFFilter) As Boolean
    ' 1. 获取当前所有条件中使用的 Filter
    Dim filterSet As ISet
    Set filterSet = IDFFilterColCondition_getAllFilters()

    ' 2. 如果没有使用 Filter，则返回 False
    '    如果使用了多个 Filter，则返回 False
    '    如果只使用了一个 Filter，并且使用的 Filter 和指定 Filter 相同，则返回 True
    If filterSet.Count = 0 Then
        IDFFilterColCondition_isSameFilter = False
    ElseIf filterSet.Count > 1 Then
        IDFFilterColCondition_isSameFilter = False
    Else
        IDFFilterColCondition_isUseFilter = _
            filterSet.Item(0) is filter
    End If
End Function

'-------------------------------------------
' 条件执行器内置 Function
'-------------------------------------------
'============================================
' 条件生成器
'============================================
' 添加一个 And 条件
' @param c 条件对象
' @return 返回自身, 以完成链式调用
Public Function cAND(ByRef c As IDFFilterColCondition) As DFFilterCompositeCondition
    ' 1. 检查参数
    If c Is Nothing Then
        Err.Raise 10001, Description:="parameter c must be not nothing"
        Exit Function
    End If

    If c.isUseFilter = False Then
        ' 2. 检查当前是否是全字面量条件
        If andLiteralConditions Is Nothing Then
            Set andLiteralConditions = New Collection
        End If

        andLiteralConditions.Add c

    ElseIf c.isUseSingleFilter Then
        ' 3. 检查当前是否只使用了一个 Filter
        If andSingleFilterConditions Is Nothing Then
            Set andSingleFilterConditions = New Collection
        End If

        andSingleFilterConditions.Add c
    Else
        ' 4. 到这里，说明条件中涉及了多个 DFFilter
        If andRelationFilterConditions Is Nothing Then
            Set andRelationFilterConditions = New Collection
        End If

        andRelationFilterConditions.Add c
    End If

    ' 5. 返回自身
    Set cAND = Me
End Function

' 添加一个 And 条件
' @param c 条件对象
'          如果真实类型不是 DFFilterCompositeCondition，将会手动封装一个
'               (这种情况一般是直接传递了一个条件)
' @return 返回自身, 以完成链式调用
Public Function cOR(ByRef c As IDFFilterColCondition) As DFFilterCompositeCondition
    ' 1. 检查参数
    If c Is Nothing Then
        Err.Raise 10001, Description:="parameter c must be not nothing"
        Exit Function
    End If

    If Not TypeOf c Is DFFilterCompositeCondition Then
        ' 2. 如果真实类型不是 DFFilterCompositeCondition，将会手动封装一个
        Dim executor As DFFilterCompositeCondition
        Set executor = New DFFilterCompositeCondition
        executor.cAND c

        If orConditions Is Nothing Then
            Set orConditions = New Collection
        End If

        orConditions.Add executor

    Else
        ' 3. 直接保存包装好的文件
        If orConditions Is Nothing Then
            Set orConditions = New Collection
        End If

        orConditions.Add c
    End If

    ' 4. 返回自身
    Set cOR = Me
End Function


' 收集与指定 DFFilter 相同的单 DFFilter 条件（And/Or条件都需要执行）
' 外部不可直接调用
' @param filter
Public Sub unsafeCollectSingleFilterConditions(ByRef filter As DFFilter)
    ' 1. 检查参数
    If filter Is Nothing Then
        Err.Raise 10001, Description:="parameter filter must be not nothing"
        Exit Sub
    End If

    ' 2. 缓存
    Set lastCheckedSingleFilter = filter

    ' 3. 处理单 Filter 的 And 条件
    Dim c As IDFFilterColCondition
    Dim i As Integer

    If Not andSingleFilterConditions Is Nothing Then
        ' 倒序检查
        For i = andSingleFilterConditions.Count To 1 Step -1
            Set c = andSingleFilterConditions.Item(i)

            ' 如果某个条件符合 Filter 规则，则移动条件
            If c.isSameFilter(filter) Then
                andSingleFilterConditions.Remove i

                If lastCheckedSingleFilterConditions is Nothing Then
                    Set lastCheckedSingleFilterConditions = New Collection
                End If

                lastCheckedSingleFilterConditions.Add c
            End If
        Next

        ' 如果单 Filter 条件为空，则删除集合对象
        If andSingleFilterConditions.Count = 0 Then
            Set andSingleFilterConditions = Nothing
        End If
    End If

    ' 4. 处理所有 Or 条件，各自在其内部进行单 Filter 条件收集
    If Not orConditions Is Nothing Then
        Dim executor As DFFilterCompositeCondition
        For Each executor In orConditions
            executor.unsafeCollectSingleFilterConditions filter
        Next
    End If
End Sub

' 筛选策略: 第一次筛选
'    1. 如果没有 Or 条件
'        抽取所有的和指定 Filter 相同的单 Filter 条件，并进行检查, 需要移动 And 条件到缓存中
'    2. 如果有 Or 条件
'        2.1 执行所有的和指定 Filter 相同的单 Filter 条件，并进行检查, 需要移动 And 条件到缓存中
'        2.2 对所有 Or 条件，也执行所有的和指定 Filter 相同的单 Filter 条件，并进行检查
'        2.3 筛选后重置所有的缓存内容，因为第二次筛选时如果不重新考虑第一次的条件，可能会产生错误
'            如:
'                    t1.x = 'x' AND t1.a = t2.a
'                OR (t1.a = 'a' AND t1.c = t2.c)
'            对于某一行数据，如果:
'                t1.x = 'x'  ==> False
'                t1.a = t2.a ==> True
'                t1.a = 'a'  ==> True
'                t1.c = t2.c ==> False
'            第一次筛选会保留下来，但是这一行数据是不符合规则的
'            如果第二次筛选时不考虑第一次的单 Filter 条件，那么执行 t1.a = t2.a ==> True
'            时，会误将当前条件当作正确数据，而导致结果异常
'
' 筛选后，将筛选结果写回 Filter
'
' @param filter
Public Sub extractFilterUsableRowBySingleCondition(ByRef filter As DFFilter)
    ' 1. 调用当前类 + 所有 Or 条件中的 unsafeCollectionSingleConditions 来收集单列条件
    unsafeCollectSingleFilterConditions filter

    ' 2. 创建一个 INT 类型的 IDFSeries
    Dim result As IDFSeries
    Dim innerSeries As DFSeriesInner
    Set innerSeries = New DFSeriesInner
    innerSeries.Init dType:= DF_DataType.DF_INT
    Set result = innerSeries

    ' 3. 如果当前条件执行器中没有任何条件，则立刻停止 ---> 保留所有数据
    If hasCondition = False Then
        Exit Sub
    End If

    ' 4. 遍历所有 DFFilter 中的 DataFrame 中的所有行
    Dim i As Long
    For i = 0 To filter.MainDFSize - 1
        ' 4.1 设置 filter 的活动行
        filter.unsafeSetActiveRowIndex i

        ' 4.2 检查条件，如果第 i 行的数据满足条件，则写入 IDFSeries 作为结果
        If unsafeCheckSingleFilterConditions Then
            result.appendData i
        End If
    Next

    ' 5. 将筛选结果写回 IDFSeries
    '    如果筛选结果的数量和 Filter 的数量相同，
    '    则说明所有数据都被保留了下来，这种情况下不需要保存筛选结果
    If result.Size <> filter.MainDFSize Then
        filter.unsafeSetMainDFUsableRowIndexes result
    End If

    ' 6. 如果存在 Or 条件，则在这里重置:
    If Not orConditions Is Nothing Then
        ' 6.1 重置当前条件执行器中的缓存 + 标记内容
        unsafeReset

        ' 6.2 重置所有 Or 条件中的缓存 + 标记内容
        Dim executor As DFFilterCompositeCondition
        For Each executor In orConditions
            executor.unsafeReset
        Next
    End If
End Sub

' 执行所有单 Filter 条件，检查数据是否符合规则
' 必须在 unsafeCollectSingleFilterConditions 调用之后再调用
' 外部不可直接调用
Public Function unsafeCheckSingleFilterConditions() As Boolean
    Dim c As IDFFilterColCondition

    Dim hasAndCondition As Boolean
    hasAndCondition = False
    unsafeCheckSingleFilterConditions = True

    ' 所有 And 条件是 True，则返回 True
    ' 如果某个 And 条件为 False，则检查 Or 条件，只要有一个 Or 条件是 True，则返回 True
    ' 否则返回 False

    ' 1. 检查: 全字面量的 And 条件
    '          只要有一个是 False，则立刻结束，并返回 False
    If Not andLiteralConditions Is Nothing Then
        hasAndCondition = True
        For Each c In andLiteralConditions
            If c.unsafeCheckActiveRowVal = False Then
                unsafeCheckSingleFilterConditions = False
                Exit For
            End If
        Next
    End If

    ' 2. 处理单例 And 条件
    '    当所有条件都符合规则时，返回 True
    '    只有前面的 And 条件都是 True 时才执行 !!!!!
    If unsafeCheckSingleFilterConditions = True Then
        If Not lastCheckedSingleFilterConditions is Nothing Then
            hasAndCondition = True
            For Each c In lastCheckedSingleFilterConditions
                If c.unsafeCheckActiveRowVal = False Then
                    unsafeCheckSingleFilterConditions = False
                    Exit For
                End If
            Next
        End If
    End If

    ' 3. 如果有 And 条件，并且全部检查结果都是 True，则结束
    If hasAndCondition = True And unsafeCheckSingleFilterConditions = True Then
        unsafeCheckSingleFilterConditions = True
        Exit Function
    End If

    ' 4. 检查: or条件
    '          只要有一个是 True，则立刻结束，并返回 True
    If Not orConditions Is Nothing Then
        Dim executor As DFFilterCompositeCondition

        For Each executor In orConditions
            If executor.unsafeCheckSingleFilterConditions Then
                unsafeCheckSingleFilterConditions = True
                Exit Function
            End If
        Next

        ' 如果全部 Or 条件都是 False，将会进入这里，直接返回 False
        unsafeCheckSingleFilterConditions = False
    End If
End Function

' 重置执行器内部的缓存、标记数据
Public Sub unsafeReset()
    Set lastCheckedSingleFilter = Nothing

    ' 将单 Filter 条件还原
    If Not lastCheckedSingleFilterConditions Is Nothing Then
        If lastCheckedSingleFilterConditions.Count > 0 Then

            Dim c As IDFFilterColCondition
            Dim i As Integer

            ' 倒序移动
            For i = lastCheckedSingleFilterConditions.Count To 1 Step -1
                Set c = lastCheckedSingleFilterConditions.Item(i)
                lastCheckedSingleFilterConditions.Remove i

                If andSingleFilterConditions Is Nothing Then
                    Set andSingleFilterConditions = New Collection
                End If
                andSingleFilterConditions.Add c
            Next

            ' 清除对象
            Set lastCheckedSingleFilterConditions = Nothing
        End If
    End If
End Sub

' 检查当前条件执行器中是否存在可用的条件
Private Function hasCondition() As Boolean

    If Not andLiteralConditions Is Nothing Then
        If andLiteralConditions.Count > 0 Then
            hasCondition = True
            Exit Function
        End If
    End If

    If Not andSingleFilterConditions Is Nothing Then
        If andSingleFilterConditions.Count > 0 Then
            hasCondition = True
            Exit Function
        End If
    End If

    If Not andRelationFilterConditions Is Nothing Then
        If andRelationFilterConditions.Count > 0 Then
            hasCondition = True
            Exit Function
        End If
    End If

    If Not orConditions Is Nothing Then
        If orConditions.Count > 0 Then
            hasCondition = True
            Exit Function
        End If
    End If

    If Not lastCheckedSingleFilterConditions Is Nothing Then
        If lastCheckedSingleFilterConditions.Count > 0 Then
            hasCondition = True
            Exit Function
        End If
    End If

End Function